/*
 * axis.c
 *
 *  Created on: 2022年2月4日
 *      Author: TamGitsun
 */

#include "axis.h"
#include "configs.h"
#include "graphic.h"
#include "utils.h"
#include "fsl_debug_console.h"
#include <stdio.h>


static int g_y_min = CONF_Y_AXIS_MIN_INITIALIZE_VALUE;
static int g_y_max = CONF_Y_AXIS_MAX_INITIALIZE_VALUE;
static uint32_t g_fg_color = 0x00FFFFFFU;
static uint32_t g_bg_color = 0U;
static uint32_t g_data_color = 0x00FFFF00U;
static func_on_k_tick_t_2_x_scale_label_t g_k_tick_t_2_x_scale_label = K_NULL;
static char g_y_label[256] = {0};


static void axis_clear_data(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
{
	int rect[] = {
		CONF_Y_AXIS_LABEL_WIDTH + CONF_X_AXIS_0_POINT_OFFSET,  // x
		CONF_X_AXIS_LABEL_HEIGHT + CONF_Y_AXIS_0_POINT_OFFSET,  // y
		CONF_X_AXIS_STEP * CONF_X_AXIS_N_SCALES,  // width
		APP_IMG_HEIGHT - (CONF_X_AXIS_LABEL_HEIGHT + CONF_Y_AXIS_0_POINT_OFFSET
			+ CONF_Y_AXIS_NAME_HEIGHT + CONF_AXIS_ARROW_HEIGHT)  // height
	};
	for (int i = 0; i < rect[3]; ++i)
	{
		for (int j = 0; j < rect[2]; ++j)
		{
			frameBuffer[i + rect[1]][j + rect[0]] = g_bg_color;
		}
	}
}


void draw_axis(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH], uint32_t fg_color, uint32_t bg_color)
{
	int y_max = g_y_max;
	int y_min = g_y_min;

    // Draw axises
	{
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            frameBuffer, fg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH - CONF_AXIS_ARROW_HALF_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT,
            frameBuffer, fg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH + CONF_AXIS_ARROW_HALF_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT,
            frameBuffer, fg_color);
		draw_string(CONF_Y_AXIS_LABEL_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT, g_y_label, 24,
            frameBuffer, fg_color, bg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            frameBuffer, fg_color);
		draw_line(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH - CONF_AXIS_ARROW_HEIGHT,
            CONF_X_AXIS_LABEL_HEIGHT + CONF_AXIS_ARROW_HALF_WIDTH,
            frameBuffer, fg_color);
		draw_line(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH - CONF_AXIS_ARROW_HEIGHT,
            CONF_X_AXIS_LABEL_HEIGHT - CONF_AXIS_ARROW_HALF_WIDTH,
            frameBuffer, fg_color);
		draw_string_v_center_align(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH,
            CONF_X_AXIS_LABEL_HEIGHT, CONF_X_AXIS_NAME, 24,
            frameBuffer, fg_color, bg_color);
	}

	// Draw scales
	{
		// Draw Y scales
		int y_scale_step = (y_max - y_min) / CONF_Y_AXIS_N_SCALES;
		int y_step = (APP_IMG_HEIGHT - CONF_X_AXIS_LABEL_HEIGHT -
            CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT -
			CONF_X_AXIS_0_POINT_OFFSET) /
            CONF_Y_AXIS_N_SCALES;
		int y_offset = 0;
		int y_scale = y_min;
		for (int i = 0; i < CONF_Y_AXIS_N_SCALES + 1; ++i)
		{
			draw_line(CONF_Y_AXIS_LABEL_WIDTH,
				CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
                CONF_Y_AXIS_LABEL_WIDTH - 10,
				CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
                frameBuffer, fg_color);
			char scale[16] = {0};
			snprintf(scale, sizeof(scale), "%d", y_scale);
			draw_string_v_center_right_align(CONF_Y_AXIS_LABEL_WIDTH - 10,
                CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
				scale, 24, frameBuffer, fg_color, bg_color);
			y_offset += y_step;
			y_scale += y_scale_step;
		}

		// Draw X scales
		int x_step = CONF_X_AXIS_STEP;
		int x_scale_step = CONF_X_AXIS_SCALE_STEP;
		int x_offset = 0;
		for (int i = 0; i < CONF_X_AXIS_N_SCALES + 1; ++i)
		{
			draw_line(
				CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				CONF_X_AXIS_LABEL_HEIGHT,
                CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				CONF_X_AXIS_LABEL_HEIGHT - 10,
                frameBuffer, fg_color);
			char scale[32] = {0};
			if (K_NULL != g_k_tick_t_2_x_scale_label)
			{
				g_k_tick_t_2_x_scale_label(i, scale, sizeof(scale));
			} else
			{
				k_tick_t_2_format_time_of_day(i, scale, sizeof(scale));
			}
			draw_string_h_center_align(
				CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				10, scale, 16, frameBuffer, fg_color, bg_color);
			x_offset += x_step;
		}
	}

	g_fg_color = fg_color;
	g_bg_color = bg_color;
}


#define N_G_VALUES CONF_X_AXIS_SCALE_SECTION_N_POINTS * CONF_X_AXIS_N_SCALES
static int g_values[N_G_VALUES] = {0};
static int g_index = 0;
static int g_should_clear_axis = 0;
static int g_index_loop = 0;


static inline int get_y_value(int y_scale_max, int y_scale_min, int value)
{
	float percentage = (value - y_scale_min) * 1.0 / (y_scale_max - y_scale_min);
	return (APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT
		- CONF_X_AXIS_NAME_WIDTH - CONF_Y_AXIS_0_POINT_OFFSET) * percentage
		+ CONF_X_AXIS_NAME_WIDTH + CONF_Y_AXIS_0_POINT_OFFSET;
}


void axis_push_value(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH], int value, int redraw_all)
{
	int do_update_axis = 0 != g_index_loop;
	int do_clear_axis = g_should_clear_axis || redraw_all;
	int y_max = g_y_max;
	int y_min = g_y_min;
	if (value > y_max)
	{
		g_y_max = y_max = value;
		do_update_axis = 1;
	} else if (value < y_min)
	{
		g_y_min = y_min = value;
		do_update_axis = 1;
	}
	if (do_update_axis)
	{
		do_clear_axis = 1;
		draw_axis(frameBuffer, g_fg_color, g_bg_color);
	}
	if (0 == g_should_clear_axis && g_index >= (N_G_VALUES - 1))
	{
		do_clear_axis = 1;
		g_should_clear_axis = 1;
		g_index_loop = 1;
	}
	++g_index;
	if (g_index >= N_G_VALUES)
	{
		g_index = 0;
	}
	g_values[g_index] = value;
	int x_begin = CONF_Y_AXIS_LABEL_WIDTH + CONF_X_AXIS_0_POINT_OFFSET;
	int x_offset_point = CONF_X_AXIS_STEP / CONF_X_AXIS_SCALE_SECTION_N_POINTS;
	if (g_index_loop)
	{
		axis_clear_data(frameBuffer);
		int index_begin = g_index + 1;
		if (index_begin >= N_G_VALUES)
		{
			for (int i = 0; i < N_G_VALUES - 1; ++i)
			{
				draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		} else
		{
			int offset = 0;
			for (int i = index_begin; i < N_G_VALUES - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
			draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[N_G_VALUES - 1]),
				x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[0]),
				frameBuffer, g_data_color);
			++offset;
			for (int i = 0; i < index_begin - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		}
	} else
	{
		if (do_clear_axis)
		{
			axis_clear_data(frameBuffer);
			for (int i = 0; i < g_index - 1; ++i)
			{
				draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		} else
		{
			draw_line(x_begin + (g_index - 1) * x_offset_point, get_y_value(y_max, y_min, g_values[g_index - 1]),
				x_begin + g_index * x_offset_point, get_y_value(y_max, y_min, g_values[g_index]),
				frameBuffer, g_data_color);
		}
	}
}


void axis_clear_values(void)
{
	memset(g_values, 0, sizeof(g_values));
	g_index = 0;
	g_should_clear_axis = 0;
	g_index_loop = 0;

	g_y_min = CONF_Y_AXIS_MIN_INITIALIZE_VALUE;
	g_y_max = CONF_Y_AXIS_MAX_INITIALIZE_VALUE;
}

void axis_push_value_just(int value)
{
	int y_max = g_y_max;
	int y_min = g_y_min;
	if (value > y_max)
	{
		g_y_max = y_max = value;
	} else if (value < y_min)
	{
		g_y_min = y_min = value;
	}
	if (g_index >= (N_G_VALUES - 1))
	{
		g_index_loop = 1;
	}
	++g_index;
	if (g_index >= N_G_VALUES)
	{
		g_index = 0;
	}
	g_values[g_index] = value;
}

void axis_redraw(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
{
	int x_begin = CONF_Y_AXIS_LABEL_WIDTH + CONF_X_AXIS_0_POINT_OFFSET;
	int x_offset_point = CONF_X_AXIS_STEP / CONF_X_AXIS_SCALE_SECTION_N_POINTS;
	int y_max = g_y_max;
	int y_min = g_y_min;
	axis_clear_data(frameBuffer);
	draw_axis(frameBuffer, g_fg_color, g_bg_color);
	for (int i = 0; i < g_index - 1; ++i)
	{
		draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
			x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
			frameBuffer, g_data_color);
	}

	if (g_index_loop)
	{
		int index_begin = g_index + 1;
		if (index_begin >= N_G_VALUES)
		{
			for (int i = 0; i < N_G_VALUES - 1; ++i)
			{
				draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		} else
		{
			int offset = 0;
			for (int i = index_begin; i < N_G_VALUES - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
			draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[N_G_VALUES - 1]),
				x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[0]),
				frameBuffer, g_data_color);
			++offset;
			for (int i = 0; i < index_begin - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		}
	} else
	{
		for (int i = 0; i < g_index - 1; ++i)
		{
			draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
				x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
				frameBuffer, g_data_color);
		}
	}
}

void axis_set_on_k_tick_t_2_x_scale_label(func_on_k_tick_t_2_x_scale_label_t callback)
{
	g_k_tick_t_2_x_scale_label = callback;
}

void axis_set_y_label(const char * label)
{
	snprintf(g_y_label, sizeof(g_y_label), "%s", label);
}
